package com.ggw.servlet;

import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.ggw.annotion.Controller;
import com.ggw.annotion.Qualifier;
import com.ggw.annotion.RequestMapping;
import com.ggw.annotion.Service;
import com.ggw.controller.DongNaoController;

/**
 * 核心控制类
 * @author ggw
 *
 */
@WebServlet("/DispatcherServlet")
public class DispatcherServlet extends HttpServlet {
	
	private static final long serialVersionUID = -1455132599061043791L;
	//将我们扫描的权限定类名放入集合类
	private List<String> packeNames= new ArrayList<String>();
	//注解属性 对应 各层对象实例MAP
	private Map<String, Object> instanceMaps=new HashMap<String, Object>();
	//请求url 对应 Mehod 对象
	private Map<String, Method> hanlderMaps=new HashMap<String, Method>();
    public void init() throws ServletException {
    	/*:
    	 * 扫描我们基础包上面的注解:com.dongnao 
    	 * 扫描我们的基础包之后拿到我们全限定名称 包名称+类名称
    	 * com/dongnao/controller/DongNaoController.java
    	 * 替换上面的/
    	 * com.dongnao.controller.DongNaoController
    	 * 拿到实例
    	 * 将实例注入我们各层bean 变量
    	 */
    	//扫描全包
    	scanBase("com.ggw");
    	//找到实例
    	try {
			filterAndInstance();
			//注入 springIOc
			springIoc();
			halderMaps();
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
    	//通过我们的方法连接来拿到相应的处理类是咧
    };
    //通过url找到相应的method对象进行处理
    
   private void halderMaps() throws Exception {
	   if(instanceMaps.size()<=0){
   		return;
   	}
	   //存的实例是有控制层和service层
		for(Map.Entry<String, Object> entry:instanceMaps.entrySet()){
		    if(entry.getValue().getClass().isAnnotationPresent(Controller.class)){
		    	Controller cortollerAnController=(Controller)entry.getValue().getClass().getAnnotation(Controller.class);
		    	String baseUrl=cortollerAnController.value();
		    	Method[] controllerMethods=entry.getValue().getClass().getMethods();
		    	for(Method controllerMethod:controllerMethods){
		    		if(controllerMethod.isAnnotationPresent(RequestMapping.class)){
		    			//@RequestMapping这个注解方法对象我们做事
		    			String methodUrl=((RequestMapping)(controllerMethod.getAnnotation(RequestMapping.class))).value();
		    			hanlderMaps.put("/"+baseUrl+"/"+methodUrl, controllerMethod);
		    		}else{
		    			continue;
		    		}
		    	}
		    }
			
		}
	}
	//这里就是将实例注入到spring容器里
	private void springIoc() throws Exception {
		if(instanceMaps.size()<=0){
    		return;
    	}
		for(Map.Entry<String, Object> entry:instanceMaps.entrySet()){
			Field[] fields=entry.getValue().getClass().getDeclaredFields();
			for(Field field:fields){
				if(field.isAnnotationPresent(Qualifier.class)){
					//注入
					String qualifierValue= ((Qualifier)field.getAnnotation(Qualifier.class)).value();
					field.setAccessible(true);
					field.set(entry.getValue(),instanceMaps.get(qualifierValue));
					
				}else {
					continue;
				}
			}
			
		}
	}

	//扫描全包的方法
    private void scanBase(String basePackage) {
		 URL url=this.getClass().getClassLoader().getResource("/"+replacePath(basePackage));
		 //拿到该路径下面的文件夹以及文件
		String pathFile=url.getFile();
		//最终目的是将这个路径封装成一个File 
		File file = new File(pathFile);
		String[] files=file.list();
		for(String path:files){
			//再次构造成一个file
			File eachFile= new File(pathFile+path);
			if(eachFile.isDirectory()){
				//递归应用scanBase
				scanBase(basePackage+"."+eachFile.getName());
			}else if(eachFile.isFile()){
				System.out.println("扫描类有："+basePackage+"."+eachFile.getName());
				//表示扫描到的是一个实在文件
				packeNames.add(basePackage+"."+eachFile.getName());
			}
				
			}
		}


    //将全包名称 替换成一个路径 /
    private String replacePath(String path){
    	return path.replaceAll("\\.", "/");
    }
     //拦截方法请求然后在对应请求地址找到对应的handler实例
    @SuppressWarnings({ "unchecked", "rawtypes" })
	private void filterAndInstance() throws Exception{
		//上来我们就判断集合里面是否有实例
    	if(packeNames.size()<=0){
    		return;
    	}
    	for(String className:packeNames){
    		Class ccName=Class.forName(className.replace(".class", ""));
    		
    		if(ccName.isAnnotationPresent(Controller.class)){
    			Object instance=ccName.newInstance();
    			//将实例装入Map key 
    			Controller cortollerAnController=(Controller)ccName.getAnnotation(Controller.class);
    			//通过注解对象拿到属性值  xml: key:beanID    value:class=com.d
    			String key=cortollerAnController.value();
                  //通过注解的key找到对应bean的是实例
    			instanceMaps.put(key, instance);
    			//放入Map
    		}else if(ccName.isAnnotationPresent(Service.class)){
    			Object instance=ccName.newInstance();
    			//将实例装入Map key 
    			Service serviceAnController=(Service)ccName.getAnnotation(Service.class);
    			//通过注解对象拿到属性值 
    			String key=serviceAnController.value();
                  //通过注解的key找到对应bean的是实例
    			instanceMaps.put(key, instance);
    			//放入Map
    		}else {
				//没有注解
    			continue;
			}
    	}
	}
	@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse resp)
			throws ServletException, IOException {
		// TODO Auto-generated method stub
		doPost(req,resp);
	}

	@Override
	protected void doPost(HttpServletRequest req, HttpServletResponse resp)
			throws ServletException, IOException {
		//拿到完整路径
		String uri=req.getRequestURI();
		String projectName=req.getContextPath();
		//bastURL+methodUrl
		String path=uri.replace(projectName, "");
		//方法对象
		Method method =hanlderMaps.get(path);
		PrintWriter outPrintWriter =resp.getWriter();
		if(method==null){
			outPrintWriter.write("您访问的这个资源找不到，请检查您的访问地址！");
			return;
		}
		//localhost:8088/springMvc/dongNao/insert
		String className=uri.split("/")[2];
		DongNaoController dn=(DongNaoController)instanceMaps.get(className);
		try {
			method.invoke(dn, new Object[]{req,resp,null});
		} catch (IllegalAccessException | IllegalArgumentException
				| InvocationTargetException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

}
